package es.unex.meigas.extRandomUtils;

import java.awt.geom.Point2D;
import java.util.Date;
import java.util.Random;
import java.util.Stack;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import es.unex.meigas.core.AdministrativeUnit;
import es.unex.meigas.core.ConcentricPlot;
import es.unex.meigas.core.Cruise;
import es.unex.meigas.core.DasocraticElement;
import es.unex.meigas.core.FixedRadiusPlot;
import es.unex.meigas.core.MeigasValue;
import es.unex.meigas.core.Plot;
import es.unex.meigas.core.Stand;
import es.unex.meigas.core.Tree;

public class RandomUtils {

	/**
	 * Generate new DasocraticElements on the project. First element on the 'elementsForLevel' array
	 * has the number of AdministrationUnits, the second the number of Stands, and so on.
	 *
	 * 	elementsForLevel[0] are number of levels of ADMINISTRATIVE_UNITs
		elementsForLevel[1] are number of STANDs
		elementsForLevel[2] are number of CRUISEs
		elementsForLevel[3] are number of FIXED_RADIUS_PLOTs
		elementsForLevel[4] are number of CONCENTRIC_PLOTs
		elementsForLevel[5] are number of TREEs
	 *
	 * @param model
	 * @param elementsForLevel Array of integers with the numbers of elements on each level.
	 * @param randomFill
	 */
	public static void createNewDasocraticElements(DefaultTreeModel model,
			int[] elementsForLevel,
			double dbh_mean,
			double dbh_stdDev,
			double height_mean,
			double height_stdDev,
			double radius,
			boolean randomFill){
		createNewDasocraticElements(model, elementsForLevel, elementsForLevel,
									dbh_mean, dbh_stdDev,
									height_mean, height_stdDev,
									radius, randomFill);

	}

	private static int getRandomInteger (int min, int max){

		int value = min;
		Random r =  new Random();
		return r.nextInt(max-min+1)+min;
	}

	public static void createNewDasocraticElements(DefaultTreeModel model,
														int[] minElementsForLevel,
														int[] maxElementsForLevel,
														double dbh_mean,
														double dbh_stdDev,
														double height_mean,
														double height_stdDev,
														double radius,
														boolean randomFill){

		int plotcount = 0;
		int treecount = 0;

		int levels = minElementsForLevel.length;
		if (minElementsForLevel.length != maxElementsForLevel.length){
			return;
		}

		int numElements = 0;
		DefaultMutableTreeNode parentTree = (DefaultMutableTreeNode) model.getRoot();
		DasocraticElement parentElement = (DasocraticElement) parentTree.getUserObject();

		Stack treeLevelNodes = new Stack();
		Stack auxStackNodes = new Stack();

		if (levels > 0 ) {
			// AdministrationUnit
			numElements = getRandomInteger(minElementsForLevel[0], maxElementsForLevel[0]);
			if (numElements == 0) {
				// At least one unit must be created
				numElements = 1;
			}

			for (int i = 0; i < numElements; i++){
				DasocraticElement element = new AdministrativeUnit();
				element.setName("unidad_"+i);
				element.setNotes("Notes of "+ element.getName());
				parentElement.addElement(element);
				parentElement = element;
				DefaultMutableTreeNode elementNode = new DefaultMutableTreeNode(element);
				model.insertNodeInto(elementNode, parentTree, parentTree.getChildCount());
				parentTree = elementNode;
			}
		}

		if (levels > 1) {
			// Stand
			// Only add Stands on the lower AdministrativeUnit
			numElements = getRandomInteger(minElementsForLevel[1], maxElementsForLevel[1]);
			for (int j = 0; j < numElements; j++){
				DasocraticElement element = new Stand();
				element.setName("canton_"+j);
				element.setNotes("Notes of "+ element.getName());
//				element.setEasternLimit(sEasternLimit);
//				element.setNorthernLimit(sNorthernLimit);
//				element.setSouthernLimit(sSouthernLimit);
//				element.setWesternLimit(sWesternLimit);
//				element.setSiteIndex(iSiteIndex);
//				element.setPolygon(poly);
				parentElement.addElement(element);
				DefaultMutableTreeNode elementNode = new DefaultMutableTreeNode(element);
				model.insertNodeInto(elementNode, parentTree, parentTree.getChildCount());
				treeLevelNodes.push(elementNode);
			}
		}

		if (levels > 2) {
			// Cruise
			for (; 0 < treeLevelNodes.size();){
				parentTree = (DefaultMutableTreeNode) treeLevelNodes.pop();
				DasocraticElement parent = (DasocraticElement)parentTree.getUserObject();
				numElements = getRandomInteger(minElementsForLevel[2], maxElementsForLevel[2]);
				for (int j = 0; j < numElements; j++){
					Cruise element = new Cruise();
					element.setName("muestreo_"+j);
					element.setNotes("Notes of "+element.getName());
					element.setDate(new Date());
					parent.addElement(element);
					DefaultMutableTreeNode elementNode = new DefaultMutableTreeNode(element);
					model.insertNodeInto(elementNode, parentTree, parentTree.getChildCount());
					auxStackNodes.push(elementNode);
				}
			}
		}
		treeLevelNodes.addAll(auxStackNodes);
		auxStackNodes.clear();

		if (levels > 4) {
			// Fixed_Radius_Plot and Concentric_Plot (next one)
			// fixedRadiusPlots = new FixedRadiusPlot[numElements];
			for (; 0 < treeLevelNodes.size();){
				parentTree = (DefaultMutableTreeNode) treeLevelNodes.pop();
				DasocraticElement parent = (DasocraticElement)parentTree.getUserObject();
				numElements = getRandomInteger(minElementsForLevel[3], maxElementsForLevel[3]);
				for (int j = 0; j < numElements; j++){
//					if (numElements%2 == 0){
//						FixedRadiusPlot element1 = new FixedRadiusPlot();
//						element1.setName("frp_"+plotcount);
//						element1.setNotes("Notes of "+element1.getName());
//						element1.setDate(new Date());
//						//TODO Coords of trees must be inside plot area
//						double auxX = getRandomInteger(0, 1000);
//						double auxY = getRandomInteger(0, 1000);
//						Point2D coords = new Point2D.Double(auxX, auxY);
//						element1.setCoords(coords);
////						element1.setRegenerationNotes(sRegenerationNotes);
//						element1.getRadius().setValue(radius);
//						element1.setCruiser(parent.getName());
//						parent.addElement(element1);
//						DefaultMutableTreeNode elementNode = new DefaultMutableTreeNode(element1);
//						model.insertNodeInto(elementNode, parentTree, parentTree.getChildCount());
//						auxStackNodes.push(elementNode);
//					} else {
						//numElements = getRandomInteger(minElementsForLevel[4], maxElementsForLevel[4]);
						ConcentricPlot element2 = new ConcentricPlot();
						element2.setName("parcela_"+plotcount);
						element2.setNotes("Notes of "+element2.getName());
						element2.setDate(new Date());
						element2.setCruiser(parent.getName());
						//TODO Coords of trees must be inside plot area
						double auxX = getRandomInteger(0, 1000);
						double auxY = getRandomInteger(0, 1000);
						Point2D coords = new Point2D.Double(auxX, auxY);
						element2.setCoords(coords);
						Double[] radiusArray = {new Double(5.0), new Double(10.0),
												new Double(15.0), new Double(25.0)};
						element2.setRadius(radiusArray);
						Double[] mDiamenters = {new Double(7.5), new Double(12.5),
												new Double(22.5), new Double(42.5)};
						element2.setMinAcceptableDiameters(mDiamenters);
						parent.addElement(element2);
						DefaultMutableTreeNode elementNode = new DefaultMutableTreeNode(element2);
						model.insertNodeInto(elementNode, parentTree, parentTree.getChildCount());
						auxStackNodes.push(elementNode);
					//}
					plotcount++;
				}
			}
		}

		treeLevelNodes.addAll(auxStackNodes);
		auxStackNodes.clear();

		if (levels > 5) {
			// Tree
			for (; 0 < treeLevelNodes.size();){
				parentTree = (DefaultMutableTreeNode) treeLevelNodes.pop();
				DasocraticElement parent = (DasocraticElement)parentTree.getUserObject();
				numElements = getRandomInteger(minElementsForLevel[5], maxElementsForLevel[5]);
				Plot plot = (Plot) parent;
				for (int j = 0; j < numElements; j++){
					Tree element = new Tree();
					element.setName(plot.getName()+"_arbol_"+treecount);
					element.setNotes("Notes of "+element.getName());
					double dvalue = getRandomNormal(dbh_mean, dbh_stdDev, element.getDBH());
					element.getDBH().setValue(dvalue);
					dvalue = getRandomNormal(height_mean, height_stdDev, element.getHeight());
					element.getHeight().setValue(dvalue);
					int iRadius = 0;
					if (plot instanceof ConcentricPlot) {
						ConcentricPlot cplot = (ConcentricPlot) plot;
						iRadius = (int) cplot.getMaxRadius();
					}
					else{
						FixedRadiusPlot frp = (FixedRadiusPlot) plot;
						iRadius = (int) frp.getRadius().getValue();
					}
					double dRadius = Math.random() * iRadius;
					double dAngle = Math.random() * 2. * Math.PI;
					double auxX = dRadius * Math.cos(dAngle);
					double auxY = dRadius * Math.sin(dAngle);
					Point2D coords = new Point2D.Double(plot.getCoords().getX() + auxX, plot.getCoords().getY() + auxY);
					element.setCoords(coords);
//					element.setInjuredParts(injuredParts);
//					element.setInjuryCauses(injuryCauses);
//					element.setPosition(iPosition);
//					element.setSeverity(iSeverity);
//					element.setShapeFactor(iShapeFactor);
//					element.setSpecie(sSpecie);
					parent.addElement(element);
					DefaultMutableTreeNode elementNode = new DefaultMutableTreeNode(element);
					model.insertNodeInto(elementNode, parentTree, parentTree.getChildCount());
					treecount++;
				}
			}
		} // End TREEs
		model.reload();
	}

	static double getRandomNormal(double mean, double stdDev){

		double x1, x2, w, y1;
		do{
			x1	= 2.0 * Math.random() - 1.0;
			x2	= 2.0 * Math.random() - 1.0;

			w	= x1 * x1 + x2 * x2;
		}
		while( w >= 1.0 );

		w	= Math.sqrt((-2.0 * Math.log(w)) / w);
		y1	= x1 * w;

		return (mean + stdDev * y1);

	}

	static double getRandomNormal(double mean, double stdDev, MeigasValue meigasValue){

		double value = getRandomNormal(mean, stdDev);
		if ( value < meigasValue.getMin() || value > meigasValue.getMax()){
			value = getRandomNormal(mean, stdDev, meigasValue);
		}
		return value;

	}


}
